/*
   Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
   Original sources are available at www.latestbit.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package targets.java

import commons.*
import org.latestbit.smartobjects.smartidlc.*
import org.latestbit.smartobjects.smartidlc.model.*

/**
 * @author abdulla-a
 */
public class Module extends TargetLangModule {
	   
	def Module() {		
		setModuleName("java");
	};

	def String getTargetLanguageName() {
		return "Java";
	}
	
	def generateFileName(namedObject, String prefix="", String postfix="") {
		return prefix+namedObject.getName()+postfix+".java";
	}
	
	def generateQualifiedTypeName ( IDLDataType idlType, String prefix = "", String postfix = "" ) {
		if(idlType.isImported()) {
			return idlType.userObject.namespace.name+"."+prefix+idlType.relativeName +postfix;
		}
		else {					
			return prefix+idlType.name+postfix;
		}
	}
	
	def generateQualifiedName ( element, String prefix = "", String postfix = "" ) {
		if(targetNamespace!= element.namespace) {
			return element.namespace.name+"."+prefix+element.name+postfix;
		}
		else {					
			return prefix+element.name+postfix;
		}
	}	
	
	def toJavaType(IDLDataType idlType, onlyObj = false, disableInterfaces = false) {
		if(idlType.typeId.equals(IDLDataType.DataTypeId.QUALIFIED_NAME )) {
			if ( idlType.userObject instanceof IDLTypeDef &&
				!idlType.userObject.refDataType.typeId.equals(IDLDataType.DataTypeId.QUALIFIED_NAME)) {
				return idlType.name;
			}
			else {
				if(!disableInterfaces)
					return generateQualifiedTypeName ( idlType, "I" );
				else
					return generateQualifiedTypeName ( idlType );
			}
		}
		if(idlType.typeId.equals(IDLDataType.DataTypeId.PRIMITIVE ))
			return toPrimitiveJavaType(idlType.primitiveType, onlyObj);		
		if(idlType.typeId.equals(IDLDataType.DataTypeId.COMPLEX ))
			return toComplexJavaType(idlType.complexType, onlyObj, disableInterfaces);
	}
	
	def toPrimitiveJavaType(IDLPrimitiveType primitiveType, onlyObj = false) {
		switch ( primitiveType.typeId ) {
			case IDLPrimitiveType.TypeId.BOOLEAN:
				if(!onlyObj)
					return "boolean";
				else
					return "Boolean";
				break;
			case IDLPrimitiveType.TypeId.BYTE:
			case IDLPrimitiveType.TypeId.UNSIGNED_BYTE:
				if(!onlyObj)
					return "byte";
				else
					return "Byte";
				break;
			case IDLPrimitiveType.TypeId.CHAR:
			case IDLPrimitiveType.TypeId.WCHAR:
				if(!onlyObj)				
					return "char";
				else
					return "Character";
				break;
			case IDLPrimitiveType.TypeId.DOUBLE:
				if(!onlyObj)
					return "double";
				else
					return "Double";
				break;
			case IDLPrimitiveType.TypeId.FLOAT:
				if(!onlyObj)				
					return "float";
				else
					return "Float";
				break;
			case IDLPrimitiveType.TypeId.INT16:
			case IDLPrimitiveType.TypeId.UINT16:
				if(!onlyObj)				
					return "short";
				else
					return "Short";
				break;
			case IDLPrimitiveType.TypeId.INT32:
			case IDLPrimitiveType.TypeId.UINT32:
				if(!onlyObj)				
					return "int";
				else
					return "Integer";				
				break;
			case IDLPrimitiveType.TypeId.INT64:
			case IDLPrimitiveType.TypeId.UINT64:
				if(!onlyObj)				
					return "long";
				else
					return "Long";				
				break;
			case IDLPrimitiveType.TypeId.BIGINTEGER:
				return "BigInteger";				
				break;
			case IDLPrimitiveType.TypeId.BIGDECIMAL:
				return "BigDecimal";				
				break;														
			case IDLPrimitiveType.TypeId.STRING:
			case IDLPrimitiveType.TypeId.WSTRING:				
				return "String";
				break;
			case IDLPrimitiveType.TypeId.DATETIME:				
				return "Date";
				break;				
		}
	}
	
	def toPrimitiveJavaType(IDLPrimitiveType.TypeId primitiveTypeId, onlyObj = false) {
		IDLPrimitiveType type = new IDLPrimitiveType();
		type.typeId = primitiveTypeId;
		return toPrimitiveJavaType(type, onlyObj);
	}
		
	
	def toComplexJavaType(IDLComplexType complexType, onlyObj = false, disableInterfaces = false) {
		switch ( complexType.complexTypeId ) {
			case IDLComplexType.ComplexTypeId.ARRAY:
				if(complexType.complexItemDataType.typeId==IDLDataType.DataTypeId.COMPLEX 
					&& complexType.complexItemDataType.complexType.complexTypeId == IDLComplexType.ComplexTypeId.ARRAY)
					return "Collection< ${toJavaType(complexType.complexItemDataType, true, disableInterfaces)} >";
				else
					return toJavaType(complexType.complexItemDataType, false, disableInterfaces)+"[]";					
				break;
			case IDLComplexType.ComplexTypeId.LIST:
				return "Collection< ${toJavaType(complexType.complexItemDataType, true, disableInterfaces)} >";
				break;
			case IDLComplexType.ComplexTypeId.MAP:
				return "Map< ${toPrimitiveJavaType(complexType.complexKeyDataType, true)}, ${toJavaType(complexType.complexItemDataType, true, disableInterfaces)} >";
				break;				
		}
	}
	

	def toResultJavaType ( IDLInterfaceOperation oper ) {
		if(oper.resultType==null) {
			return "void";
		}
		else {
			if ( ! isServerGenerationMode() || !oper.interfaceReferenceResult ) {
				if( !isServerGenerationMode() && oper.isResultSessionId() ) {
					return "SessionId < "+toJavaType(oper.resultType)+" >";					
				}
				else
					return toJavaType(oper.resultType);					
			}
			else {
				return generateQualifiedTypeName ( oper.resultType, "", "Skeleton" );
			}
		}
	}
	
	def toParamJavaType ( IDLInterfaceOperationParam param ) {
		if ( isServerGenerationMode() || !param.isInterfaceReference() ) {
			if(!isServerGenerationMode() && param.isSessionId())
				return "SessionId < "+toJavaType(param.type)+" >";
			else
				return toJavaType(param.type);
		}
		else {
			return generateQualifiedTypeName ( param.type, "", "Skeleton" );
		}
	}
	
	def toJavaFullName( IDLElement element ) {
		String result = element.getName(); 
		if(element.getNamespace()!=null)
			result = element.getNamespace().getName() + "." +result;
		return result;		
	}
	
	def isNullable( IDLDataType idlType ) {
		if(idlType.typeId.equals(IDLDataType.DataTypeId.QUALIFIED_NAME )) {
			return true;
		}
		if(idlType.typeId.equals(IDLDataType.DataTypeId.PRIMITIVE ))
			return isNullable(idlType.primitiveType);
		if(idlType.typeId.equals(IDLDataType.DataTypeId.COMPLEX ))
			return true;
		return false;
	}
	
	def isNullable( IDLPrimitiveType primitiveType ) {
		switch ( primitiveType.typeId ) {
			case IDLPrimitiveType.TypeId.BIGINTEGER:
			case IDLPrimitiveType.TypeId.BIGDECIMAL:
			case IDLPrimitiveType.TypeId.STRING:
			case IDLPrimitiveType.TypeId.WSTRING:
			case IDLPrimitiveType.TypeId.DATETIME:
				return true;
				break;
		}
		return false;
	}
	
	def generateDirectories() {
		def parts = this.targetNamespace.name.split("\\.");
		def javaOutputDir = this.outputDir;
		def dir = new File ( javaOutputDir );
		if (!dir.exists()) {
			if(!dir.mkdir()) {
				throw new IDLTargetGenerationException("Unable to create directory: ${dir.absolutePath}");
			}
		}
		parts.each {
			javaOutputDir+=it+File.separator;
			dir = new File ( javaOutputDir );
			if (!dir.exists()) {
				if(!dir.mkdir()) {
					throw new IDLTargetGenerationException("Unable to create directory: ${dir.absolutePath}");
				}
			}
		}
		this.outputDir = javaOutputDir;
		reporter.onMessage("--> Java output dir: ${javaOutputDir}");
	}	
	
	
	
	def generateTypeDef ( typeDef ) {
		def binding = [ "typeDef" : typeDef ];
		if ( !typeDef.refDataType.typeId.equals(IDLDataType.DataTypeId.QUALIFIED_NAME) || typeDef.refDataType.isEncrypted() ) {
			processTemplate ("types/typedef", binding, generateFileName(typeDef));
		}
		else {
			processTemplate ("types/inherited_typedef_interface", binding, generateFileName(typeDef, "I"));
			processTemplate ("types/inherited_typedef_impl", binding, generateFileName(typeDef));
		}
	}
		
	
	def generateStructure ( structure ) {
		def binding = [ "structure" : structure ];
		processTemplate ("types/structure_interface", binding, generateFileName(structure, "I"));
		processTemplate ("types/structure_impl", binding, generateFileName(structure));
	}
	
	
	def generateChoice ( choice ) {
		def binding = [ "choice" : choice ];
		processTemplate ("types/choice_interface", binding, generateFileName(choice, "I"));
		processTemplate ("types/choice_impl", binding, generateFileName(choice));
	}
	
	
	def generateEnum ( enumeration ) {
		def binding = [ "enumeration" : enumeration ];
		processTemplate ("types/enum_interface", binding, generateFileName(enumeration, "I"));
		processTemplate ("types/enum_impl", binding, generateFileName(enumeration));
	}
		
	def generateException ( exc ) {
		def binding = [ "exc" : exc ];
		processTemplate ("types/exception_impl", binding, generateFileName(exc));
	}	
	
	def generateInterface ( iface ) {
		def binding = [ "iface" : iface ];
		processTemplate ("types/client_interface", binding, generateFileName(iface, "I"));
		processTemplate ("types/client_callback_interface", binding, generateFileName(iface, "I", "Callback"));
		processTemplate ("types/client_interface_impl", binding, generateFileName(iface));
		this.setServerGenerationMode ( true );
		try {
			processTemplate ("types/server_async_interface", binding, generateFileName(iface, "I", "SmartObject"));
			processTemplate ("types/server_sync_interface", binding, generateFileName(iface,"I", "SyncSmartObject"));
			processTemplate ("types/server_async_callback_interface", binding, generateFileName(iface, "I", "ServerCallback"));
			processTemplate ("types/server_async_callback_interface_impl", binding, generateFileName(iface, "", "ServerCallback"));
			processTemplate ("types/server_async_skeleton", binding, generateFileName(iface, "", "Skeleton"));				
			processTemplate ("types/server_sync_skeleton", binding, generateFileName(iface, "", "SyncSkeleton"));
		}
		finally {
			this.setServerGenerationMode ( false );
		}
	}	
}
